package rank

import (
	"GoServer/common"
	"GoServer/models"
	"GoServer/utils/config"
	"GoServer/utils/databases"
	"GoServer/utils/debug"
	"GoServer/utils/tool"
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
	"sync"
	"time"
)

var GameName string

func BeginRankGame() {
	GameName = common.GameProcessKey()
	debug.Logln(debug.Debug, "启动排行榜处理")
	/// 定时同步redis排行榜数据到sql
	tool.Timer(
		time.Duration(3)*time.Second,
		time.Duration(30*30)*time.Second,
		SyncDataToMongoDB,
		"")

	/// 定时清理redis排行数据
	tool.Timer(
		time.Duration(3)*time.Second,
		time.Duration(60*60)*time.Second,
		CleanupRedisRankData,
		"")

	go ScheduleMonthlyClear(databases.Rediss, databases.MongDBClient)
	go WaitAccessTokenRequestSuccess()
}

func WaitAccessTokenRequestSuccess() {
	delayTime := config.GetConfig("app").GetInt("douyin.DelayRefreshTime")
	<-time.After(time.Duration(delayTime+1) * time.Second)

	// 设置当前生效的世界榜单数据
	//SetWorldVersion()

	/// 开启同步世界榜单数据
	//refreshWorldRank()
}

//func refreshWorldRank() {
//	tool.Timer(
//		time.Duration(30),
//		time.Duration(50)*time.Second,
//		SyncWorldRankToTiktok,
//		"")
//}

// 添加 redis 分数到排行榜
func AddScore(rdb *redis.Client, users models.UserRankBasic) error {
	err := rdb.ZAdd(context.Background(), GameName+"world_rank", &redis.Z{
		Score:  float64(users.Energy),
		Member: users.PlayerId,
	}).Err()
	if err != nil {
		debug.Logln(debug.Error, "添加用户分数到 Redis 时出错: ", err)
	}

	return err
}

// / 获取前 N 名的玩家
func GetTopUsers(rdb *redis.Client, top int64) ([]redis.Z, error) {
	return rdb.ZRevRangeWithScores(context.Background(), GameName+"world_rank", 0, top-1).Result()
}

func SaveUsersToMongoAndRedis(rdb *redis.Client, mongoClient *mongo.Client, users []models.UserRankBasic) {
	dbname := config.GetConfig("app").GetString("mongdb.database")
	collection := mongoClient.Database(dbname).Collection(GameName + "user_world_rank")

	var wg sync.WaitGroup
	for _, user := range users {
		wg.Add(1)
		go func(user models.UserRankBasic) {
			defer wg.Done()

			filter := map[string]interface{}{"playerId": user.PlayerId}
			update := map[string]interface{}{
				"$set": user,
			}

			_, err := collection.UpdateOne(context.Background(), filter, update, options.Update().SetUpsert(true))
			if err != nil {
				debug.Logln(debug.Error, "插入或更新数据到 MongoDB 时出错: ", err)
			}

			if err := AddScore(rdb, user); err != nil {
				debug.Logln(debug.Error, "")
			}

			err = rdb.HSet(context.Background(), GameName+"rank_data", user.PlayerId, user).Err()
			if err != nil {
				debug.Logf(debug.Error, "数据缓存至redis, playerId: [%s]", user.PlayerId)
			}
		}(user)
	}

	wg.Wait()
	debug.Logln(debug.Error, "用户数据已保存到 MongoDB 并同步到 Redis 完成")
}

// / 从redis 同步数据到 MongoDB
func SyncDataToMongoDB(param interface{}) (result bool) {
	var wg sync.WaitGroup
	topUsers, err := GetTopUsers(databases.Rediss, 100)
	if err != nil {
		debug.Logln(debug.Error, "获取 redis 排行榜数据错误:", err)
	}

	dbname := config.GetConfig("app").GetString("mongdb.database")
	collection := databases.MongDBClient.Database(dbname).Collection(GameName + "user_world_rank")

	for i, user := range topUsers {
		wg.Add(1)
		go func(i int, user redis.Z) {
			defer wg.Done()

			userData := models.UserRankBasic{
				PlayerId: user.Member.(string),
				Energy:   uint64(user.Score),
			}

			filter := map[string]interface{}{"playerId": userData.PlayerId}
			update := map[string]interface{}{
				"$set": userData,
			}

			_, err := collection.UpdateOne(context.Background(), filter, update, options.Update().SetUpsert(true))
			if err != nil {
				debug.Logln(debug.Error, "插入或更新数据到 MongoDB 时出错: ", err)
			}
		}(i, user)
	}

	wg.Wait()
	debug.Logln(debug.Info, "数据同步完成")

	return true
}

// 获取玩家在排行榜的排名
func GetPlayerRankNum(redisClient *redis.Client, playerId string) (int64, error) {
	rank, err := redisClient.ZRevRank(context.Background(), GameName+"world_rank", playerId).Result()
	if err != nil {
		if err == redis.Nil {
			return -1, fmt.Errorf("playerId not found in the rank data")
		}
		return -1, fmt.Errorf("failed to get player rank from Redis: %w", err)
	}

	return rank + 1, nil
}

// /获取排行排名
func GetTopTenRank(redisClient *redis.Client, count int64) ([]models.UserRankBasic, error) {
	rankList, err := databases.Rediss.ZRevRangeWithScores(context.Background(), GameName+"world_rank", 0, count-1).Result()
	if err != nil {
		debug.Logln(debug.Error, "Failed to get rank list from Redis:", err)
		return nil, err
	}
	debug.Logln(debug.Debug, "获取到玩家的长度：", len(rankList))
	var players []models.UserRankBasic
	// 遍历排行榜列表
	for rank, member := range rankList {
		playerId := member.Member.(string)
		debug.Logln(debug.Debug, "Rank:", rank+1, "PlayerId:", playerId, "Score:", int(member.Score))
		playerData, err := GetPlayerDataFromRedis(databases.Rediss, playerId)
		playerData.RankNumber = uint(rank + 1)
		if err == nil {
			players = append(players, *playerData)
		} else {
			debug.Logln(debug.Error, err)
		}
	}

	return players, nil
}

// / 取出某个玩家数据
func GetPlayerDataFromRedis(redisClient *redis.Client, playerId string) (*models.UserRankBasic, error) {
	value, err := redisClient.HGet(context.Background(), GameName+"rank_data", playerId).Result()
	if err != nil {
		return nil, fmt.Errorf("failed to get player data from Redis: %w", err)
	}

	var playerData *models.UserRankBasic

	if err := json.Unmarshal([]byte(value), &playerData); err != nil {
		// 处理反序列化失败的情况
		return nil, fmt.Errorf("Failed to unmarshal player data:", err)
	}

	return playerData, nil
}

// 获取排行榜数据
func GetPlayerRankDataFromRedis(redisClient *redis.Client, playerId string) (float64, error) {
	value, err := redisClient.ZScore(context.Background(), GameName+"world_rank", playerId).Result()
	if err != nil {
		return 0, fmt.Errorf("failed to get player data from Redis: %w", err)
	}

	return value, nil
}

// / 更新某个玩家数据
func UpdateEnergyInRedis(redisClient *redis.Client, rank *models.UserRankBasic) error {
	playerData, err := json.Marshal(rank)
	if err != nil {
		debug.Logln(debug.Error, "解析当前玩家数据错误", err)
		return err
	}
	updated, err := redisClient.HSetNX(context.Background(), GameName+"rank_data", rank.PlayerId, playerData).Result()
	if err != nil {
		return fmt.Errorf("failed to update energy in Redis: %w", err)
	}

	_, err = redisClient.ZAdd(context.Background(), GameName+"world_rank", &redis.Z{
		Member: rank.PlayerId,
		Score:  float64(rank.Energy),
	}).Result()
	if err != nil {
		return fmt.Errorf("failed to update energy in ZAddRedis: %w", err)
	}

	if updated {
		debug.Logln(debug.Debug, "Player energy updated in Redis")
	} else {
		debug.Logln(debug.Debug, "Player does not exist in Redis, inserting new data")
		// 在此处执行插入新数据的操作
		err := redisClient.HMSet(context.Background(), GameName+"rank_data", rank.PlayerId, playerData).Err()
		if err != nil {
			return fmt.Errorf("failed to insert player data to Redis: %w", err)
		}

		debug.Logln(debug.Debug, "Player data inserted to Redis")
	}

	return nil
}

// 定时清理 Redis 过期数据
func CleanupRedisRankData(param interface{}) (result bool) {
	totalCount, err := databases.Rediss.ZCard(context.Background(), GameName+"world_rank").Result()
	if err != nil {
		debug.Logln(debug.Error, "获取 Redis 排行榜总数时出错:", err)
		return false
	}

	if totalCount > 100 {
		// 获取第 10 名的分数
		scores, err := databases.Rediss.ZRevRangeWithScores(context.Background(), GameName+"world_rank", 100, 100).Result()
		if err != nil || len(scores) == 0 {
			debug.Logln(debug.Error, "获取 Redis 排行榜第 10 名分数时出错:", err)
			return false
		}

		// 获取第 10 名的最低分数
		minScore := scores[0].Score

		// 删除分数小于第 10 名的所有元素，保留前 10 名
		err = databases.Rediss.ZRemRangeByScore(context.Background(), GameName+"world_rank", "-inf", fmt.Sprintf("(%f", minScore)).Err()
		if err != nil {
			debug.Logln(debug.Error, "清理 Redis 排行榜数据时出错:", err)
			return false
		}
	}

	debug.Logln(debug.Info, "Redis, 排行榜数据清理完成！")
	return true
}

// 清空 Redis 排行榜数据
func ClearRedisData(rdb *redis.Client) {
	err := rdb.Del(context.Background(), GameName+"world_rank").Err()
	rdb.Del(context.Background(), "rank_data").Err()
	if err != nil {
		debug.Logln(debug.Error, "清空 Redis 数据时出错:", err)
	} else {
		debug.Logln(debug.Info, "Redis 数据已清空")
	}
}

// 清空 MongoDB 集合数据
func ClearMongoDBData(mongoClient *mongo.Client) {
	dbname := config.GetConfig("app").GetString("mongdb.database")
	collection := mongoClient.Database(dbname).Collection(GameName + "user_world_rank")
	err := collection.Drop(context.Background())
	if err != nil {
		log.Println("清空 MongoDB 集合数据时出错:", err)
	} else {
		log.Println("MongoDB 数据已清空")
	}
}

// 每月 1 号 6 点定时清空 Redis 和 MongoDB 的数据
func ScheduleMonthlyClear(rdb *redis.Client, mongoClient *mongo.Client) {
	for {
		now := time.Now()
		nextClearTime := time.Date(now.Year(), now.Month()+1, 1, 6, 0, 0, 0, now.Location())
		durationUntilNextClear := time.Until(nextClearTime)

		debug.Logf(debug.Error, fmt.Sprintf("下次清空数据时间: %s", nextClearTime))

		// 等待直到下次清空时间
		time.Sleep(durationUntilNextClear)

		// 执行清空操作
		ClearRedisData(rdb)
		ClearMongoDBData(mongoClient)
	}
}
